import _thread
import aiohttp
import asyncio
import configparser

from flet import (
    app,
    AppBar,
    Checkbox,
    Container,
    Divider,
    ElevatedButton,
    GridView,
    Icon,
    IconButton,
    icons,
    margin,
    Page,
    padding,
    Row,
    Text,
    TextField,
    View,
    ThemeMode,
    ScrollMode
)
import gc
import os
import threading

import CentralController
import CleanCache
import DatabaseOperation
import PackagedInfo
import PicUnit
import PixivAPI
import PublicConstant

# TODO: 性能下降有点厉害，而且性能下降只和pic_grid_view.controls的长度有关
new_keyword = ""
backend_controller = CentralController.CentralController()
search_thread: threading.Thread | None = None


def data_fetch(search_keyword: str) -> tuple[PackagedInfo.PackagedPicInfo] | None:
    res = DatabaseOperation.fetch_data_by_keyword(
        search_keyword, backend_controller.settings
    )
    return res


async def download_thumbs_in_a_thread_async(
    _url: str, _filepath: str, _api: PixivAPI.PixivAPI, _ss: aiohttp.ClientSession
):
    byte_stream_of_pic = await _api.get_pic_thumbnail_async(_url, _ss)
    with open(_filepath, "wb") as _f:
        _f.write(byte_stream_of_pic)


async def check_and_download_thumbnails_async(qd: tuple[PackagedInfo.PackagedPicInfo]):
    api = PixivAPI.PixivAPI(
        backend_controller.settings.proxies, backend_controller.browser_cookies
    )
    async with aiohttp.ClientSession() as ss:
        ss: aiohttp.ClientSession
        async with asyncio.TaskGroup() as tg:
            tg: asyncio.TaskGroup  # To enable auto completion in text editor
            for index, each in enumerate(qd):
                filepath = f"{PublicConstant.thumbnail_path}{each.id}.jpg"
                # Download thumbnails if they do not exist
                if not os.path.exists(filepath):
                    tg.create_task(
                        download_thumbs_in_a_thread_async(
                            each.thumbnail_url, filepath, api, ss
                        )
                    )


def main(page: Page):
    page.title = "Pixiv Crawler"
    page.theme_mode = ThemeMode.LIGHT
    page.scroll = ScrollMode.ALWAYS
    page.auto_scroll = False
    page.window_min_width = 720
    page.window_min_height = 500

    def lock_ui(need_lock: bool):
        nonlocal search_input
        nonlocal search_button
        if search_button.disabled == need_lock:
            return
        search_button.disabled = need_lock
        search_input.disabled = need_lock
        search_button.text = "Waiting" if need_lock else "Search"
        page.update()

    def view_pop(_):
        page.views.pop()
        top_view = page.views[-1]
        page.go(top_view.route)

    def open_settings(_):
        def save_settings(_):
            nonlocal checkbox_allow_r18
            nonlocal checkbox_allow_normal
            nonlocal checkbox_allow_ai
            nonlocal bookmark_threshold
            nonlocal proxy_settings
            assert(checkbox_allow_r18.value is not None)
            assert(checkbox_allow_ai.value is not None)
            assert(checkbox_allow_normal.value is not None)
            backend_controller.settings.allow_r18 = checkbox_allow_r18.value
            backend_controller.settings.allow_ai = checkbox_allow_ai.value
            backend_controller.settings.allow_normal = checkbox_allow_normal.value
            backend_controller.settings.proxies = (
                None
                if proxy_settings.value == ""
                else {"http": proxy_settings.value, "https": proxy_settings.value}
            )
            try:
                backend_controller.settings.like_num_threshold = int(
                    bookmark_threshold.value
                )
            except Exception as _e:
                print(type(_e), _e)
            backend_controller.settings.save_settings_to_file()
            view_pop(None)
            if new_keyword != "":
                refresh_after_save_settings()

        checkbox_allow_r18 = Checkbox(label="R-18")
        checkbox_allow_normal = Checkbox(label="Safe")
        checkbox_allow_ai = Checkbox(label="NovelAI")
        bookmark_threshold = TextField(
            hint_text="Artworks with bookmark count less than this value will not be displayed"
        )
        proxy_settings = TextField(hint_text="http://ip_address:port")

        # Load current settings
        checkbox_allow_r18.value = backend_controller.settings.allow_r18
        checkbox_allow_ai.value = backend_controller.settings.allow_ai
        checkbox_allow_normal.value = backend_controller.settings.allow_normal
        proxy_settings.value = (
            ""
            if backend_controller.settings.proxies is None
            else backend_controller.settings.proxies["http"]
        )
        bookmark_threshold.value = f"{backend_controller.settings.like_num_threshold}"

        def clear_cache(_):
            nonlocal pic_grid_view
            CleanCache.clear_cache()
            pic_grid_view.controls.clear()
            print("All cache has been cleared.")

        page.views.append(
            View(
                controls=[
                    AppBar(
                        title=Text("Settings"),
                        leading=Icon(icons.SETTINGS_OUTLINED),
                        actions=[
                            Container(
                                Row([IconButton(icons.CLOSE, on_click=view_pop)]),
                                padding=padding.symmetric(vertical=3),
                            )
                        ],
                    ),
                    Text("Filter Settings", size=20, weight=flet.FontWeight.BOLD),
                    Row(
                        controls=[
                            checkbox_allow_r18,
                            checkbox_allow_normal,
                            checkbox_allow_ai,
                        ]
                    ),
                    Text(
                        "Bookmark Threshold Settings",
                        size=20,
                        weight=flet.FontWeight.BOLD,
                    ),
                    bookmark_threshold,
                    Text("HTTP Proxy Settings", size=20, weight=flet.FontWeight.BOLD),
                    proxy_settings,
                    Text("Clear Cache", size=20, weight=flet.FontWeight.BOLD),
                    ElevatedButton(
                        text="Clear Cache",
                        tooltip="Delete the database, search histories and downloaded thumbnails",
                        on_click=clear_cache,
                    ),
                    Divider(),
                    Row(
                        controls=[
                            ElevatedButton("Save", on_click=save_settings),
                            ElevatedButton("Discard", on_click=view_pop),
                        ]
                    ),
                ]
            )
        )
        page.update()

    def start_searching(_):
        global search_thread
        global new_keyword
        nonlocal search_input
        nonlocal pic_grid_view
        lock_ui(True)
        stop_searching()
        if search_input.value.strip() == "":
            lock_ui(False)
            return
        new_keyword = search_input.value.strip()
        if search_thread is not None and search_thread.is_alive():
            CentralController.write_to_shared(b"stop\n")
            search_thread.join()
        CentralController.write_to_shared(b"start\n")
        search_thread = threading.Thread(
            target=backend_controller.search, args=(new_keyword,)
        )
        search_thread.start()
        pic_grid_view.controls.clear()

    def stop_searching():
        global search_thread
        if search_thread is not None and search_thread.is_alive():
            CentralController.write_to_shared(b"stop\n")
            search_thread.join()

    def update_with_new_pic():
        global new_keyword
        nonlocal pic_grid_view

        query = data_fetch(new_keyword)
        if query is not None:
            # check_and_download_thumbnails(query_data)
            asyncio.run(check_and_download_thumbnails_async(query))

            gc.disable()

            pic_grid_view.controls = [
                PicUnit.PicUnit(each.id, each.like_num, each.is_r18) for each in query
            ]

            page.update()
            gc.enable()

    def refresh_after_save_settings():
        global new_keyword
        nonlocal pic_grid_view

        query = data_fetch(new_keyword)
        if query is not None:
            query: tuple[PackagedInfo.PackagedPicInfo]
            # check_and_download_thumbnails(query_data)
            asyncio.run(check_and_download_thumbnails_async(query))

            pic_grid_view.controls = [
                PicUnit.PicUnit(each.id, each.like_num, each.is_r18) for each in query
            ]
            page.update()

    def refresh_thumbs(_):
        pass
        # nonlocal pic_grid_view
        # for i, each in enumerate(pic_grid_view.controls):
        #     # each == pic_grid_view.controls[i]: FletStackWithID, each.controls[0]: Image
        #     # print(each.pic_id, isinstance(each.controls[0], ProgressRing))
        #     if isinstance(each.controls[0], ProgressRing) \
        #             and os.path.exists(f'{PublicConstant.thumbnail_path}{each.pic_id}.jpg'):
        #         pic_grid_view.controls[i].controls[0] = Image(
        #             src=f'/{each.pic_id}.jpg',
        #             border_radius=border_radius.all(10)
        #         )
        #     if i % 50 == 0:
        #         page.update()
        # page.update()

    search_input = TextField(hint_text="Search Keyword", on_submit=start_searching)
    search_button = ElevatedButton("Search", on_click=start_searching)

    page.appbar = AppBar(
        title=Text("Pixiv Crawler"),
        leading=IconButton(
            icons.BUG_REPORT,
            on_click=refresh_thumbs,
            tooltip="Force Refresh Thumbnails",
        ),
        actions=[
            Container(
                Row(
                    [
                        search_input,
                        search_button,
                        IconButton(
                            icons.SETTINGS, on_click=open_settings, tooltip="Settings"
                        ),
                    ]
                ),
                margin=margin.symmetric(vertical=3),
            )
        ],
    )

    pic_grid_view = GridView(
        max_extent=250,  # Flex
        child_aspect_ratio=1.0,
        spacing=5,
        run_spacing=5,
        padding=padding.only(right=14),
    )
    page.add(pic_grid_view)

    page.update()

    def update_closure():
        while True:
            CentralController.has_new_page.acquire()
            lock_ui(False)
            update_with_new_pic()

    _thread.start_new_thread(update_closure, tuple())


if __name__ == "__main__":
    if not os.path.isdir(PublicConstant.thumbnail_path):
        os.mkdir(PublicConstant.thumbnail_path)
    conf = configparser.ConfigParser()
    try:
        conf.read(PublicConstant.search_history_path, encoding="utf-8")
        if not conf.has_section(PublicConstant.search_history_section_name):
            conf[PublicConstant.search_history_section_name] = dict()
            with open(PublicConstant.search_history_path, "w") as f:
                conf.write(f)
    except Exception as e:
        print(type(e), e)
        conf[PublicConstant.search_history_section_name] = dict()
        with open(PublicConstant.search_history_path, "w") as f:
            conf.write(f)

    app(target=main, assets_dir=PublicConstant.thumbnail_path)
